/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved.
 */

import cluster from 'cluster';
import EventEmitter from 'events';
import process from 'process';

import { noop, cloneDeep } from '@baize/wdk';
import { AdaptorError, MixAdaptor } from '@ohos/hvigor-logger';

import { CUR_HVIGOR_VERSION } from '../../../cli/wrapper/wrapper-const.js';
import { HookType } from '../../../common/const/hook-const.js';
import { HvigorConfigLoader } from '../../../common/util/hvigor-config-loader.js';
import { ReportServiceImpl } from '../../common/report/report-service-impl.js';
import { hvigorTrace } from '../../common/trace/hvigor-trace.js';
import { traceManager } from '../../common/trace/trace-manager.js';
import { setRunning } from '../../daemon/cluster/watch-config-file.js';
import { WORKER_PROCESS_EVENT_ID } from '../../daemon/cluster/worker-process-event-id.js';
import { hvigorCore } from '../../external/core/hvigor-core.js';
import { defaultModelRegistry } from '../../external/default-tooling-model-bean-registry.js';
import { BuildResult } from '../../external/models/build-result.js';
import { HvigorLogger } from '../../log/hvigor-log.js';
import { MarkEventCategory, MarkEventState, MarkEventType } from '../../metrics/event/mark-event.js';
import { MetricFactory } from '../../metrics/metric-factory.js';
import { MetricService } from '../../metrics/metric-service.js';
import { HvigorCliOptions } from '../../util/options/hvigor-cli-options.js';
import { formatTime } from '../../util/time-util.js';
import { ProjectCacheService } from '../cache/project-cache-service.js';
import { AnalyzeMode, coreParameter } from '../data/global-core-parameters.js';
import { globalData, resetStartData } from '../data/global-data.js';
import { TcbStore } from '../pool/store/tcb-store.js';
import { normalWorker } from '../pool/worker-pool/watch-worker.js';
import { WorkerPoolFactory } from '../pool/worker-pool/worker-pool-factory.js';
import { taskControlCenter } from '../task/core/task-control-center.js';
import { projectTaskDag } from '../task/core/task-directed-acyclic-graph.js';
import { HVIGOR_PROCESS_EVENT_ID } from './event/event-id-options.js';
import { HvigorLifecycleHook } from './hook/hvigor-lifecycle-hook.js';
import { outputPluginSyncInfo } from './sync.js';

const _log = HvigorLogger.getLogger('hvigor-process');

class HvigorBuildProcess extends EventEmitter {
  public taskBeginTime: [number, number] | undefined;
  public hasTaskDone = false;

  constructor() {
    super();
    process.on('message', (message: any) => {
      if (message.type === HVIGOR_PROCESS_EVENT_ID.FAILED) {
        this.emit(HVIGOR_PROCESS_EVENT_ID.FAILED, message?.reason);
      }
    });
  }

  /**
   * 初始化hvigorProcess,包括创建监听等
   *
   */
  public init(): void {
    this.once(HVIGOR_PROCESS_EVENT_ID.FINISHED, async () => {
      await hvigorProcessListenerFactory.getProcessListenerByEventName(HVIGOR_PROCESS_EVENT_ID.FINISHED)();
    });

    this.once(HVIGOR_PROCESS_EVENT_ID.FAILED, async (reason?: Error | string) => {
      await hvigorProcessListenerFactory.getProcessListenerByEventName(HVIGOR_PROCESS_EVENT_ID.FAILED)(reason);
    });
  }

  public startProcessMonitor(): void {
    if (coreParameter.startParams.analyze === AnalyzeMode.ADVANCED) {
      ReportServiceImpl.getInstance().startProcessMonitor();
    }
  }

  /**
   * 开始启动hvigorProcess的生命周期执行
   *
   */
  public setTaskBeginTime(): void {
    this.taskBeginTime = process.hrtime();
  }

  /**
   * hvigor process正常结束
   *
   */
  public close() {
    this.hasTaskDone = false;
    _log.debug('hvigor build process will be closed.');
    this.removeAllListeners();
  }

  /**
   * hvigor process异常结束
   *
   */
  public error(error: Error, taskLog?: HvigorLogger) {
    const currentLog: HvigorLogger = taskLog ?? _log;
    currentLog.debug('hvigor build process will be closed with an error.');
    if (error.name === AdaptorError.NAME) {
      currentLog.error(error.message);
    } else {
      if (error?.message) {
        currentLog.printErrorWithAdaptorErrorMessage(new MixAdaptor(error.message).getErrorMessage());
      } else {
        currentLog.error(error.message);
      }
    }

    // 用户开启打印stacktrace时,则将error完整打印出来,包含堆栈
    if (error.stack) {
      if (coreParameter.startParams.printStackTrace === true) {
        currentLog.error(error.stack);
      } else {
        currentLog.debug('ERROR: stacktrace =', error.stack);

        // 日志需要记录堆栈信息
        _log._printStackErrorToFile(error.stack);
      }
    }

    // 统一在生命周期开始的地方处理未被捕获和处理的异常,并退出process
    hvigorProcess.emit(HVIGOR_PROCESS_EVENT_ID.FAILED, error);
  }
}

/**
 * 根据hvigorProcess定义的事件,返回不同的listener(function)
 */
class HvigorProcessListenerFactory {
  public getProcessListenerByEventName(eventName: string): Function {
    switch (eventName) {
      case HVIGOR_PROCESS_EVENT_ID.FINISHED:
        return this.getFinishedEventListener();
      case HVIGOR_PROCESS_EVENT_ID.FAILED:
        return this.getFailedEventListener();
      default:
        return noop;
    }
  }

  private getFailedEventListener(): (reason?: Error | string) => void {
    return async (reason?: Error | string) => {
      const env = globalData.cliEnv;
      const buildFinishedHookEvent = MetricFactory.createDurationEvent('build finish', '', '');
      await HvigorLifecycleHook.getInstance().runHook(
        HookType.buildFinished,
        new BuildResult(reason ? (typeof reason === 'string' ? new Error(reason) : reason) : null, ReportServiceImpl.getInstance().getReport()),
        buildFinishedHookEvent,
      );
      const taskEndTime: [number, number] = process.hrtime(hvigorProcess.taskBeginTime);
      this.historyDataRecord(hvigorProcess.taskBeginTime, MarkEventState.FAILED);
      const realTime = formatTime(taskEndTime);

      _log.error(`BUILD FAILED in ${realTime}`);
      process.exitCode = -1;

      wrapUpBeforeExit(env.cwd, taskEndTime[0] * 1e9 + taskEndTime[1]);
      if (cluster.isWorker) {
        process.send!({
          type: WORKER_PROCESS_EVENT_ID.FAILED,
        });
      }
      exitHvigor(process.exitCode);
    };
  }

  private getFinishedEventListener(): () => void {
    const opts = globalData.cliOpts;
    const env = globalData.cliEnv;
    const buildFinishedHookEvent = MetricFactory.createDurationEvent('build finish', '', '');
    if (opts.sync) {
      return async () => {
        await HvigorLifecycleHook.getInstance().runHook(
          HookType.buildFinished,
          new BuildResult(null, ReportServiceImpl.getInstance().getReport()),
          buildFinishedHookEvent,
        );
        outputPluginSyncInfo();
        if (cluster.isWorker) {
          process.send!({
            type: WORKER_PROCESS_EVENT_ID.FINISHED,
          });
        }
        exitHvigor();
      };
    } else {
      return async () => {
        await HvigorLifecycleHook.getInstance().runHook(
          HookType.buildFinished,
          new BuildResult(null, ReportServiceImpl.getInstance().getReport()),
          buildFinishedHookEvent,
        );
        const taskEndTime: [number, number] = process.hrtime(hvigorProcess.taskBeginTime);
        const realTime = formatTime(taskEndTime);

        let taskState = MarkEventState.SUCCESS;
        if (hvigorProcess.hasTaskDone) {
          _log.info(`BUILD SUCCESSFUL in ${realTime}`);
        } else {
          const cliOpts = globalData.cliOpts as HvigorCliOptions;
          _log.printErrorExit('TASK_WAS_NOT_FOUND_IN_THE_PROJECT', [cliOpts._, hvigorCore.getProject()?.getName()]);
          _log.error(`BUILD FAILED in ${realTime}`);
          taskState = MarkEventState.FAILED;
        }
        this.historyDataRecord(hvigorProcess.taskBeginTime, taskState);
        wrapUpBeforeExit(env.cwd, taskEndTime[0] * 1e9 + taskEndTime[1]);
        if (cluster.isWorker) {
          process.send!({
            type: WORKER_PROCESS_EVENT_ID.FINISHED,
          });
        }
        exitHvigor();
      };
    }
  }

  private historyDataRecord(taskBeginTime: [number, number] | undefined, state: MarkEventState): void {
    if (!taskBeginTime) {
      return;
    }
    const taskName = globalData.cliOpts._.toString();
    const isBuild = taskName !== 'clean';
    const markEvent = MetricFactory.createMarkEvent(taskName, '');
    let completeCommand;
    if (coreParameter.startParams.daemon) {
      completeCommand = globalData.cliOpts.completeCommand;
    } else {
      completeCommand = process.argv.slice(2).join(' ');
    }
    const globalDataCliOptsCopy = cloneDeep(globalData.cliOpts);
    delete globalDataCliOptsCopy.env;
    markEvent.setCompleteCommand(`${JSON.stringify(globalDataCliOptsCopy)};${completeCommand ?? taskName}`);
    markEvent.setHvigorVersion(CUR_HVIGOR_VERSION);
    markEvent.setMarkType(MarkEventType.HISTORY);
    markEvent.setNodeVersion(process.version);
    markEvent.setCategory(isBuild ? MarkEventCategory.BUILD : MarkEventCategory.CLEAN);
    markEvent.start(MarkEventState.RUNNING, this.resolveTaskTime(taskBeginTime));
    markEvent.stop(state);
  }

  private resolveTaskTime(time: [number, number]): number {
    if (!time || !time[0] || !time[1]) {
      return 0;
    }
    return time[0] * 1e9 + time[1];
  }
}

const hvigorProcessListenerFactory = new HvigorProcessListenerFactory();

function clearProcessContext() {
  // 清理normalWorker内部维护的状态信息
  normalWorker.reset();

  // 清理缓存的report数据
  MetricService.getInstance().clear();

  // 清理上一次全局的任务图
  projectTaskDag.clear();

  taskControlCenter.clear();

  // 清理上一次构建sync注册的数据模型
  defaultModelRegistry.clear();

  // 重新设置Hvigor全局对象的上下文
  hvigorCore.reset();

  // 关闭此次buildProcess
  hvigorProcess.close();

  // 重置startParameters和startEnvironment
  resetStartData();
}

/**
 * 退出前的扫尾工作,包括写缓存,打点等
 *
 * @param {string} cwd
 * @param {number} realTime
 */
function wrapUpBeforeExit(cwd: string, realTime: number) {
  // 写缓存
  const project = hvigorCore.getProject();

  // 这里为空说明project对象还没有创建出来就报错结束了，此处必须跳过执行 不然会报错导致后面的report.json无法生成出来
  if (project) {
    const projectCacheService = ProjectCacheService.getInstance(project);
    projectCacheService.close();
  }

  traceEnd(realTime);

  if (coreParameter.startParams.analyze !== AnalyzeMode.FALSE) {
    // 启动报告服务
    const reportService = ReportServiceImpl.getInstance();
    reportService.report();
  }
}

function traceEnd(totalTime: number) {
  hvigorTrace.traceTotalTime(totalTime);
  hvigorTrace.transmitDataToManager();
  traceManager.flush();
}

/**
 * 退出当前单次构建, 清理上下文信息
 */
function exitHvigor(exitCode?: number) {
  hvigorCore.writeMetaProperties();
  hvigorCore.cleanCache();
  globalData.clean();
  HvigorConfigLoader.clean();
  HvigorLogger.clean();
  ReportServiceImpl.getInstance().stopProcessMonitor();

  setImmediate(() => {
    const clearTcbStore = (res: boolean) => {
      if (res) {
        TcbStore.clear();
      }
    };
    if (WorkerPoolFactory.getDefaultWorkerPool().isResident() && coreParameter.startParams.daemon) {
      WorkerPoolFactory.getDefaultWorkerPool().recycle().then(clearTcbStore);
    } else {
      WorkerPoolFactory.getDefaultWorkerPool().terminate().then(clearTcbStore);
    }

    // daemon模式下,需要清理构建上下文
    clearProcessContext();
  });
  setRunning(false);

  // no-daemon模式下mac/linux需要额外关闭监听使process能够正常关闭，临时逻辑，还需分析清楚哪块监听没有释放
  if (!coreParameter.startParams.daemon) {
    process.removeAllListeners();
  }
}

export const hvigorProcess = new HvigorBuildProcess();
